package net.chrisrichardson.ec2deployer.applications

import net.chrisrichardson.ec2deployer.cluster.ClusterSpec
import net.chrisrichardson.ec2deployer.core.Application
import net.chrisrichardson.ec2deployer.core.AwsUtilities
import net.chrisrichardson.ec2deployer.core.EC2Server
import net.chrisrichardson.ec2deployer.core.S3Synchronizer
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import net.chrisrichardson.ec2deployer.cluster.EbsVolume
import net.chrisrichardson.ec2deployer.cluster.ClusterSpec;
import net.chrisrichardson.ec2deployer.cluster.ClusterSpec
import net.chrisrichardson.ec2deployer.core.AWSProperties
import net.chrisrichardson.ec2deployer.core.ProcessExecutionException;

class MySqlServer extends Application {

    long id

    ClusterSpec clusterSpec
    Object master
    boolean configured

    // 5.0
    def serviceName = "mysqld"
    def baseDir = "/var/lib"

    // 5.1
    // def serviceName = "mysql"
    // def baseDir = "/usr"

    protected MySqlServer() {

    }

    MySqlServer(EC2Server host, ClusterSpec clusterSpec) {
        super(host)
        this.clusterSpec = clusterSpec
    }

    def getSchemaSpec() {
        clusterSpec.schemaSpec
    }

    MySqlServer(EC2Server host, master, ClusterSpec clusterSpec) {
        super(host)
        this.master = master
        this.clusterSpec = clusterSpec
    }

    public addClientApplication(Application clientApplication) {
        super.addClientApplication(clientApplication)
    }

    def grantAccessToClient(client) {
        assert host.isRunning()
        schemaSpec.users.each {
            exec("mysql $schemaSpec.name", "GRANT ALL PRIVILEGES ON *.* TO '${it.key}'@'${client.host.privateDnsName}' IDENTIFIED BY \"${it.value}\";")
        }
    }

    def configureAsMaster() {
        configureDatabase()
    }

    void configureDatabase() {
        ignoringFailure ("stop failed - ignoring") {
            stopService serviceName
        }

        if (clusterSpec.ebsVolume) {
            setUpEbs()
            baseDir = "/vol/lib"
        }

        writeFile fileName: "/etc/my.cnf", templateName: "/templates/master.my.cnf",  templateArgs: [baseDir: baseDir]

        startService serviceName
    }

    void setUpEbs() {
        EbsVolume ebsVolume = clusterSpec.ebsVolume
        boolean newVolume = !ebsVolume.useExistingVolume
        if (newVolume) {
            host.createEbsVolume(ebsVolume)
        }
        host.attachEbsVolume(ebsVolume)

        exec command: "dos2unix|bash",
             templateName: "/templates/configureebs.sh",
             templateArgs: [ebsVolume: ebsVolume, requiresInitialization: (newVolume && !ebsVolume.snapshotId) ]
    }


    def dbRecreate() {
        exec command: "mysql -u root", templateName: "/templates/dropSchema.sql", templateArgs: [schemaSpec: schemaSpec]
        dbCreate()
    }

    def dbCreate() {
        logger.debug("Creating schema and users on master")

        exec command: "mysql -u root", templateName: "/templates/createSchema.sql", templateArgs: [schemaSpec: schemaSpec]

        executeSchemaScripts()
    }

    void dbCreateOnStartup() {
        if (!configured) {
            dbCreate()
            configured = true
        }
    }

    def executeSchemaScripts() {
        executeSqlScripts(schemaSpec.scripts)
    }

    void executeSqlScripts(List<String> scripts) {
        def s3Scripts = scripts.findAll { it.startsWith "s3://" }
        if (s3Scripts) {
            AWSProperties awsProperties = host.awsProperties;
            host.ssh("cat > .awssecret", "${awsProperties.accessKey} ${awsProperties.secretKey}")
            for (script in s3Scripts) {
                exec "s3get ${script.substring( "s3://".length())} /var/tmp/foo.sql"
                exec "mysql $schemaSpec.name < /var/tmp/foo.sql"
            }
        }

        def localScripts = scripts.findAll { !it.startsWith("s3://") }
        if (localScripts) {
            S3Synchronizer s3Synchronizer = new S3Synchronizer(host.awsProperties)
            def s3Bucket = "tmp-${schemaSpec.name}-dml"
            s3Synchronizer.uploadToS3(localScripts, clusterSpec.bucketPrefix, s3Bucket)
            String dir = "/tmp/$s3Bucket"
            s3Synchronizer.downloadFromS3(host, clusterSpec.bucketPrefix, s3Bucket, dir)
            localScripts.each {
                def localScriptName = dir + "/" + it.substring(1 + it.lastIndexOf("/"))
                if (localScriptName.endsWith(".gz"))
                    exec("gunzip < $localScriptName | mysql $schemaSpec.name")
                else
                    exec("mysql $schemaSpec.name < $localScriptName")
            }
        }
    }

    def configureAsSlave(int slaveServerId) {
        master.grantAccessToSlave(this)
        writeFile fileName: "/etc/my.cnf", templateName: "/templates/slave.my.cnf", templateArgs: [slaveServerId: slaveServerId, baseDir: baseDir]
        restartService serviceName
        if (!configured) {
            exec command: "mysql", templateName: "/templates/configureSlave.sql", templateArgs: [master: master]
            configured = true
        }
    }

    def grantAccessToSlave(MySqlServer slave) {
        logger.debug "Granting access to slave on master"
        exec command: "mysql", templateName: "/templates/grantAccessToSlave.sql", templateArgs: [slave: slave]

    }

    void dbSave(String s3Path) {
      AwsUtilities awsUtilities = new AwsUtilities(host);
      def slashIndex = s3Path.indexOf("/")
      assert slashIndex > 0 : "Format must be bucketName/fileName: $s3Path" 
      def bucketName = s3Path.substring(0, slashIndex)
      awsUtilities.mkdir(bucketName)
      host.ssh("mysqldump ${schemaSpec.name}  > /var/tmp/mysqldump.sql")
      awsUtilities.s3put(s3Path, "/var/tmp/mysqldump.sql")
    }

    void dbRestore(String s3Path) {
      AwsUtilities awsUtilities = new AwsUtilities(host);
      awsUtilities.s3get(s3Path, "/var/tmp/mysqldump.sql")
      host.ssh("mysql ${schemaSpec.name}  < /var/tmp/mysqldump.sql")
    }

    void initlbpool(List slaves) {
        exec command: "mysql lbpool", templateName: "/templates/initlbpool.sql", templateArgs: [master: host, slaves: slaves.host]
    }

    void configureBackup(String bucket, String objectPrefix, boolean master) {
        new AwsUtilities(host).initializeAwsSecretFile()
        writeFile fileName: "/root/mysql2s3", templateName: "/templates/mysql2s3.sh",
                templateArgs: [schema: schemaSpec.name, bucket: bucket, objectPrefix: objectPrefix]
        exec "chmod u+x /root/mysql2s3"
        exec "mkdir /var/tmp/mysql2s3"
        exec "chmod og-xrw /var/tmp/mysql2s3"
        def cronExpr = master ? "10 2 * * *" : "*/10 * * * *"
        writeFile fileName: "/etc/cron.d/mysql2s3", templateName: "/templates/mysql2s3.cron.d",
                templateArgs: [cronExpr: cronExpr]

    }

}

